'use strict';

angular.module('core')

.directive('accordion', function() {
    return {
        restrict: 'E',
        replace : true,
        transclude : true,
        template : '<div class="accordion" ng-transclude></div>',
        require: ['?ngModel', 'accordion'],
        link: function (scope, element, attrs, controllers) {
            var ngModelCtrl = controllers[0];
            var accordionCtrl = controllers[1];

            if (ngModelCtrl) {
                accordionCtrl.registerViewChangeListeners(function () {
                    ngModelCtrl.$setViewValue(accordionCtrl.activeIndexes());
                });
                ngModelCtrl.$formatters.push(function (modelValue) {
                    if (angular.isArray(modelValue)) {
                        accordionCtrl.setActive(modelValue);
                    } else {
                        var activeIndexes = accordionCtrl.activeIndexes();
                        if (angular.isArray(activeIndexes)) {
                            if (activeIndexes.indexOf(modelValue * 1) === -1) {
                                accordionCtrl.setActive(modelValue * 1);
                            }
                        } else if (activeIndexes !== modelValue * 1) {
                            accordionCtrl.setActive(modelValue * 1);
                        }
                    }
                    return modelValue;
                });
            }
        },
        controller : ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            var _targets = [],
                _toggles = [],
                _viewChangeListeners = [],
                _active = [];

            this.registerViewChangeListeners = function (fn) {
                _viewChangeListeners.push(fn);
            };
            this.registerTarget = function (element) {
                _targets.push(element);
            };
            this.registerToggle = function (element) {
                _toggles.push(element);
            };
            this.setActive = function (value) {
                var disallowToggle = !!$attrs.disallowToggle;
                if (angular.isArray(value)) {
                    _active = value;
                } else if (!disallowToggle && isActive(value)) {
                    deactivateItem(value);
                } else {
                    activateItem(value);
                }
                render();
            };
            this.getTargetIndex = function (element) {
                return _targets.indexOf(element);
            };
            this.getToggleIndex = function (element) {
                return _toggles.indexOf(element);
            };
            this.activeIndexes = function () {
                var allowMultiple = !!$attrs.allowMultiple;
                if (allowMultiple) {
                    return _active;
                }
                return _active.length === 1 ? _active[0] : -1;
            };

            angular.element(window).on('resize', render);
            $scope.$on('$destroy', function () {
                angular.element(window).off('resize', render);
            });

            function isActive (value) {
                return _active.indexOf(value) !== -1;
            }
            function deactivateItem (value) {
                var index = _active.indexOf(value);
                if (index !== -1) {
                    _active.splice(index, 1);
                }
            }
            function activateItem (value) {
                var allowMultiple = !!$attrs.allowMultiple;
                if (!allowMultiple) {
                    _active.splice(0, 1);
                }
                if (_active.indexOf(value) === -1) {
                    _active.push(value);
                }
            }
            function render () {
                var domHeight = $element.height(),
                    headerHeight = parseInt($attrs.headerHeight, 0),
                    activeNum = _active.length,
                    margin = 15;

                angular.forEach(_viewChangeListeners, function (fn) {
                    fn();
                });

                if (activeNum === 0) {
                    angular.forEach(_targets, function (target, index) {
                        updateElement(
                            target, 
                            headerHeight,
                            (headerHeight + margin) * index
                        );
                    });
                    return;
                }
                var activeHeight = (domHeight - (_targets.length - activeNum) * headerHeight  - (_targets.length - 1) * margin) / activeNum;
                angular.forEach(_targets, function (target, index) {
                    var before = findBefore(index);
                    updateElement(
                        target, 
                        isActive(index) ? activeHeight : headerHeight, 
                        activeHeight * before.active + headerHeight * before.unActive + margin * index
                    );
                });
            }
            function updateElement (element, height, top) {
                var dom = element[0],
                    style = dom && dom.style;
                if (style) {
                    style.webkitTransitionDuration =
                        style.MozTransitionDuration =
                            style.msTransitionDuration =
                                style.OTransitionDuration =
                                    style.transitionDuration = '300ms';
                    style.height = height + 'px';
                    style.top = top + 'px';
                }
            }
            function findBefore (index) {
                var result = {
                    active: 0,
                    unActive: 0
                };
                for (var i = 0; i < index; i++) {
                    if (_active.indexOf(i) !== -1) {
                        result.active++;
                    } else {
                        result.unActive++;
                    }
                }
                return result;
            }
        }]
    };
});